import os
# -*- coding: utf-8 -*-# 文件编码声明

# 添加DDoS防御相关依赖
from flask_limiter import Limiter
from flask_limiter.util import get_remote_address
import time
import datetime

# 导入其他必要库
import secrets
from datetime import datetime, timedelta
import random
import os
import uuid
import shutil
from sqlalchemy import text
from flask_mail import Mail, Message
from flask import Flask, render_template, request, redirect, url_for, flash, send_from_directory, abort
from flask_sqlalchemy import SQLAlchemy
from flask_login import LoginManager, UserMixin, login_user, logout_user, login_required, current_user
from werkzeug.security import generate_password_hash, check_password_hash
from werkzeug.utils import secure_filename

# 初始化Flask应用
app = Flask(__name__)
# 设置应用密钥，用于加密会话数据
app.config['SECRET_KEY'] = secrets.token_hex(16)

# 配置DDoS防御
# 初始化Limiter，用于请求频率限制
limiter = Limiter(
    get_remote_address,  # 使用客户端IP作为限制键
    app=app,
    default_limits=["200 per day", "50 per hour"],  # 默认请求频率限制
    storage_uri="memory://"  # 使用内存存储请求记录
)

# 定义IP黑名单，存储被禁止访问的IP地址
BLACKLISTED_IPS = set()

# 记录请求时间的字典，用于检测高频请求
REQUEST_TIMES = {}

# DDoS防御中间件，在每次请求前执行
@app.before_request
def ddos_protection():
    # 获取客户端IP地址
    client_ip = get_remote_address()

    # 检查是否在黑名单中，如果在则拒绝访问
    if client_ip in BLACKLISTED_IPS:
        abort(403)  # 返回403 Forbidden错误

    # 记录请求时间
    current_time = time.time()
    if client_ip not in REQUEST_TIMES:
        REQUEST_TIMES[client_ip] = []
    REQUEST_TIMES[client_ip].append(current_time)

    # 移除过期的请求记录（超过10秒）
    REQUEST_TIMES[client_ip] = [t for t in REQUEST_TIMES[client_ip] if current_time - t < 10]

    # 检查请求频率（10秒内超过30次请求则加入黑名单）
    if len(REQUEST_TIMES[client_ip]) > 30:
        BLACKLISTED_IPS.add(client_ip)
        # 记录黑名单事件到日志文件
        with open('ddos_blacklist.log', 'a') as f:
            f.write(f'{datetime.datetime.now()}: IP {client_ip} blacklisted due to high request rate\n')
        abort(403)

# 管理员移除黑名单IP的路由
@app.route('/admin/remove_blacklist/<ip>', methods=['POST'])
@login_required  # 要求用户已登录
def remove_blacklist(ip):
    # 检查是否为管理员用户
    if not getattr(current_user, 'is_admin', False):
        flash('非管理员用户禁止访问')
        return redirect(url_for('index'))
    
    # 移除IP黑名单
    if ip in BLACKLISTED_IPS:
        BLACKLISTED_IPS.remove(ip)
        flash(f'IP {ip} 已从黑名单中移除')
        # 记录移除事件到日志
        with open('ddos_blacklist.log', 'a') as f:
            f.write(f'{datetime.datetime.now()}: IP {ip} removed from blacklist by admin {current_user.username}\n')
    else:
        flash(f'IP {ip} 不在黑名单中')
    
    return redirect(url_for('admin_dashboard'))

# 显示黑名单的路由
@app.route('/admin/blacklist')
@login_required  # 要求用户已登录
def blacklist():
    # 检查是否为管理员用户
    if not getattr(current_user, 'is_admin', False):
        flash('非管理员用户禁止访问')
        return redirect(url_for('index'))
    
    # 渲染黑名单页面，传递黑名单IP列表
    return render_template('admin_blacklist.html', blacklisted_ips=BLACKLISTED_IPS)

# 配置数据库
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///网盘.db'  # SQLite数据库文件路径
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False  # 禁用修改跟踪以提高性能

# 配置文件上传
app.config['UPLOAD_FOLDER'] = 'uploads'  # 文件上传目录
app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024  # 16MB，限制文件上传大小

# 添加定时任务支持
from apscheduler.schedulers.background import BackgroundScheduler
import atexit
import datetime
import secrets

# 初始化调度器
scheduler = BackgroundScheduler()
scheduler.start()

# 确保在应用退出时关闭调度器
atexit.register(lambda: scheduler.shutdown())

# 定义定时任务装饰器

def scheduled_task(hours=12):
    """定时任务装饰器，用于注册周期性执行的任务

    Args:
        hours (int): 任务执行的时间间隔（小时），默认为12小时

    Returns:
        decorator: 装饰器函数
    """
    def decorator(func):
        scheduler.add_job(
            func,
            'interval',  # 间隔执行
            hours=hours,  # 执行间隔
            args=[app],  # 传递应用实例作为参数
            max_instances=1  # 最多允许1个实例同时执行
        )
        return func
    return decorator

# 修改create_admin函数为可被调度的任务
@scheduled_task(hours=12)
def scheduled_create_admin(app):
    """定时任务：创建或更新管理员账号，每12小时执行一次

    Args:
        app: Flask应用实例
    """
    with app.app_context():
        # 导入User模型和db对象
        from app import User, db
        
        # 检查是否已存在管理员账号
        admin = User.query.filter_by(is_admin=True).first()
        if admin:
            print('管理员账号已存在，将更新密码')
            # 生成新密码
            password = secrets.token_urlsafe(16)
            admin.set_password(password)
            db.session.commit()
        else:
            # 创建管理员账号
            username = 'admin'
            email = ''
            # 生成随机密码
            password = secrets.token_urlsafe(16)

            admin = User(username=username, email=email, is_admin=True)
            admin.set_password(password)
            db.session.add(admin)
            db.session.commit()

            print(f'管理员账号创建成功:')
        username = admin.username

        print(f'用户名: {username}')
        print(f'密码: {password}')
        print(f'请妥善保管此密码，登录后可修改')

        # 更新管理员凭证文件
        update_admin_credentials(username, password)

# 发送管理员凭证邮件的函数

def send_admin_credentials_email(username, password):
    """发送管理员账号信息邮件

    Args:
        username (str): 管理员用户名
        password (str): 管理员密码
    """
    from flask_mail import Message
    from app import mail, app
    from config_loader import app_config

    # 获取管理员收件邮箱
    admin_email = app_config['mail'].get('admin_recipient', '')
    if not admin_email:
        print('警告: 未配置管理员收件邮箱，无法发送邮件')
        return

    # 创建邮件消息
    msg = Message('管理员账号信息', recipients=[admin_email])
    msg.body = f'管理员账号信息\n\n用户名: {username}\n密码: {password}\n创建时间: {datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")}\n\n请妥善保管此密码，登录后可修改。'

    # 发送邮件
    try:
        with app.app_context():
            mail.send(msg)
        print(f'管理员账号信息已发送到邮箱: {admin_email}')
    except Exception as e:
        print(f'发送邮件失败: {str(e)}')

# 更新管理员凭证文件的函数

def update_admin_credentials(username, password):
    """更新管理员凭证文件并发送邮件

    Args:
        username (str): 管理员用户名
        password (str): 管理员密码
    """
    # 写入凭证文件
    with open('admin_credentials.txt', 'w', encoding='utf-8') as f:
        f.write(f'管理员账号信息\n')
        f.write(f'用户名: {username}\n')
        f.write(f'密码: {password}\n')
        f.write(f'创建时间: {datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")}\n')
    print(f'管理员账号信息已保存到 admin_credentials.txt 文件')
    # 发送邮件
    send_admin_credentials_email(username, password)

# 保留原有的命令行命令，方便手动执行
@app.cli.command('create-admin')
def create_admin():
    """命令行命令：手动创建或更新管理员账号"""
    scheduled_create_admin(app)


# 邮箱配置
# 从配置文件中加载邮箱设置
from config_loader import app_config

# 应用邮箱配置
app.config['MAIL_SERVER'] = app_config['mail']['server']  # 邮件服务器地址
app.config['MAIL_PORT'] = app_config['mail']['port']  # 邮件服务器端口
app.config['MAIL_USE_SSL'] = app_config['mail']['use_ssl']  # 是否使用SSL加密
app.config['MAIL_USERNAME'] = app_config['mail']['username']  # 邮箱用户名
app.config['MAIL_PASSWORD'] = app_config['mail']['password']  # 邮箱授权码
app.config['MAIL_DEFAULT_SENDER'] = app_config['mail']['default_sender']  # 默认发件人

# 重要提示：
# - 不要使用邮箱登录密码，必须使用客户端授权码
# - 确保授权码没有过期或被撤销
# - 如果认证仍然失败，检查是否开启了两步验证或其他安全设置
# - 邮箱配置信息已从config.json或config.ini文件加载

# 初始化邮件服务
mail = Mail(app)

# 存储验证码的字典，用于密码重置等功能
verify_codes = {}

# 确保上传文件夹存在
os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True)

# 初始化数据库
db = SQLAlchemy(app)

# 初始化登录管理器
login_manager = LoginManager()
login_manager.init_app(app)
login_manager.login_view = 'login'  # 设置登录视图

# 定义用户模型
class User(UserMixin, db.Model):
    """用户模型，存储用户账户信息"""
    id = db.Column(db.Integer, primary_key=True)  # 主键
    username = db.Column(db.String(50), unique=True, nullable=False)  # 用户名，唯一且非空
    email = db.Column(db.String(100), unique=True, nullable=True)  # 邮箱，唯一且可空
    password_hash = db.Column(db.String(128), nullable=False)  # 密码哈希，非空
    files = db.relationship('File', backref='owner', lazy=True)  # 与File模型的一对多关系
    reset_token = db.Column(db.String(100))  # 密码重置令牌
    reset_token_expiry = db.Column(db.DateTime)  # 密码重置令牌过期时间
    is_admin = db.Column(db.Boolean, default=False)  # 是否为管理员
    is_banned = db.Column(db.Boolean, default=False)  # 是否被封禁

    def set_password(self, password):
        """设置用户密码（哈希存储）

        Args:
            password (str): 原始密码
        """
        self.password_hash = generate_password_hash(password)

    def check_password(self, password):
        """验证用户密码

        Args:
            password (str): 待验证的密码

        Returns:
            bool: 密码是否正确
        """
        return check_password_hash(self.password_hash, password)

# 定义文件模型
class File(db.Model):
    """文件模型，存储用户上传的文件信息"""
    id = db.Column(db.Integer, primary_key=True)  # 主键
    filename = db.Column(db.String(200), nullable=False)  # 文件名，非空
    filepath = db.Column(db.String(500), nullable=False)  # 文件路径，非空
    size = db.Column(db.Integer, nullable=False)  # 文件大小，非空
    upload_date = db.Column(db.DateTime, default=datetime.datetime.utcnow)  # 上传日期，默认为当前时间
    user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)  # 外键，关联用户
    share_token = db.Column(db.String(100), unique=True)  # 分享令牌，唯一
    share_expiry = db.Column(db.DateTime)  # 分享过期时间

# 用户加载器
@login_manager.user_loader
def load_user(user_id):
    """根据用户ID加载用户对象

    Args:
        user_id (str): 用户ID字符串

    Returns:
        User: 用户对象或None
    """
    return db.session.get(User, int(user_id))

# 添加数据库表字段更新逻辑
with app.app_context():
    """应用上下文启动时，检查并更新数据库表结构"""
    # 检查并添加或修改email字段
    inspector = db.inspect(db.engine)
    if 'user' in inspector.get_table_names():
        columns = [c['name'] for c in inspector.get_columns('user')]
        if 'email' not in columns:
            # 添加带唯一性约束且允许NULL的列
            db.session.execute(text('ALTER TABLE user ADD COLUMN email VARCHAR(100) UNIQUE'))
            db.session.commit()
            # 为现有行设置唯一默认值
            users = db.session.query(User).all()
            for i, user in enumerate(users):
                # 为每个现有用户设置唯一的邮箱值
                user.email = f'user{i}@example.com'
            db.session.commit()
        else:
            # 检查email列是否允许NULL
            email_column = next((c for c in inspector.get_columns('user') if c['name'] == 'email'), None)
            if email_column and not email_column['nullable']:
                # SQLite不支持直接修改列约束，需要创建新表并迁移数据
                # 创建临时表
                db.session.execute(text('''
                    CREATE TABLE user_new (
                        id INTEGER PRIMARY KEY AUTOINCREMENT,
                        username VARCHAR(50) NOT NULL UNIQUE,
                        email VARCHAR(100) UNIQUE,
                        password_hash VARCHAR(128) NOT NULL,
                        reset_token VARCHAR(100),
                        reset_token_expiry DATETIME,
                        is_admin BOOLEAN DEFAULT FALSE,
                        is_banned BOOLEAN DEFAULT FALSE
                    )
                '''))
                # 复制数据
                db.session.execute(text('''
                    INSERT INTO user_new (id, username, email, password_hash, reset_token, reset_token_expiry, is_admin, is_banned)
                    SELECT id, username, email, password_hash, reset_token, reset_token_expiry, is_admin, is_banned
                    FROM user
                '''))
                # 删除旧表
                db.session.execute(text('DROP TABLE user'))
                # 重命名新表
                db.session.execute(text('ALTER TABLE user_new RENAME TO user'))
                db.session.commit()
        if 'reset_token' not in columns:
            db.session.execute(text('ALTER TABLE user ADD COLUMN reset_token VARCHAR(100)'))
            db.session.commit()
        if 'reset_token_expiry' not in columns:
            db.session.execute(text('ALTER TABLE user ADD COLUMN reset_token_expiry DATETIME'))
            db.session.commit()
        if 'is_admin' not in columns:
            db.session.execute(text('ALTER TABLE user ADD COLUMN is_admin BOOLEAN DEFAULT FALSE'))
            db.session.commit()
        if 'is_banned' not in columns:
            db.session.execute(text('ALTER TABLE user ADD COLUMN is_banned BOOLEAN DEFAULT FALSE'))
            db.session.commit()

# 注意：SQLite不支持直接修改列约束为NOT NULL
# 我们在应用层面确保新用户提供唯一的邮箱值或设为None
# 对于现有用户，已设置唯一的默认邮箱值

# 路由
# 添加数据库异常处理工具函数
def handle_db_exception(e, session=None):
    """处理数据库异常的工具函数

    Args:
        e (Exception): 捕获的异常对象
        session: 数据库会话对象

    Returns:
        redirect: 重定向到首页
    """
    import traceback
    print(f"数据库异常: {str(e)}")
    print(traceback.format_exc())
    # 尝试回滚事务
    if session:
        try:
            session.rollback()
        except Exception as rollback_e:
            print(f"回滚事务失败: {str(rollback_e)}")
    flash(f'数据库操作出错: {str(e)}', 'error')
    return redirect(url_for('index'))

@app.route('/@vite/client')
def vite_client():
    """处理Vite客户端请求，返回空响应"""
    return '', 204  # Return empty response with No Content status

@app.route('/')
def index():
    """首页路由

    Returns:
        render_template: 如果用户未登录，返回首页模板
        redirect: 如果用户已登录，重定向到仪表盘
    """
    if current_user.is_authenticated:
        return redirect(url_for('dashboard'))
    return render_template('index.html')

@app.route('/register', methods=['GET', 'POST'])
def register():
    """用户注册路由

    Returns:
        render_template: GET请求返回注册表单
        redirect: POST请求处理注册并 redirect到登录页或出错页
    """
    if request.method == 'POST':
        try:
            username = request.form['username']
            email = request.form.get('email', '').strip()  # 邮箱非必填，移除首尾空格
            password = request.form['password']

            if User.query.filter_by(username=username).first():
                flash('用户名已存在')
                return redirect(url_for('register'))

            # 处理邮箱，为空时设为None
            if not email:
                email = None
            else:
                # 邮箱不为空时检查是否已被注册
                if User.query.filter_by(email=email).first():
                    flash('邮箱已被注册')
                    return redirect(url_for('register'))

            user = User(username=username, email=email)
            user.set_password(password)
            db.session.add(user)
            db.session.commit()

            # 为新用户创建文件夹
            user_folder = os.path.join(app.config['UPLOAD_FOLDER'], str(user.id))
            os.makedirs(user_folder, exist_ok=True)

            flash('注册成功，请登录')
            return redirect(url_for('login'))
        except Exception as e:
            return handle_db_exception(e, db.session)

    return render_template('register.html')

@app.route('/login', methods=['GET', 'POST'])
def login():
    """用户登录路由

    Returns:
        render_template: GET请求返回登录表单
        redirect: POST请求处理登录并 redirect到仪表盘或管理员面板
    """
    if request.method == 'POST':
        username = request.form['username']
        password = request.form['password']

        user = User.query.filter_by(username=username).first()
        if user and user.check_password(password):
            login_user(user)
            if user.is_admin:
                return redirect(url_for('admin_dashboard'))
            else:
                return redirect(url_for('dashboard'))

        flash('用户名或密码错误')
        return redirect(url_for('login'))

    return render_template('login.html')

@app.route('/logout')
@login_required
def logout():
    """用户登出路由

    Returns:
        redirect: 登出后重定向到首页或管理员登录页
    """
    # 检查是否是管理员登录
    is_admin_user = getattr(current_user, 'is_admin', False)
    logout_user()
    if is_admin_user:
        return redirect(url_for('admin_login'))
    return redirect(url_for('index'))

# 获取管理员动态密码路由
@app.route('/get_admin_code')
def get_admin_code():
    """生成并发送管理员动态登录密码

    Returns:
        redirect: 重定向到管理员登录页面
    """
    # 生成6位数字动态密码
    admin_dynamic_code = ''.join([str(random.randint(0, 9)) for _ in range(6)])
    expiry_time = datetime.now(datetime.UTC) + datetime.timedelta(hours=12)

    # 存储动态密码和过期时间
    global admin_code_store
    admin_code_store = {
        'code': admin_dynamic_code,
        'expiry': expiry_time
    }

    # 发送动态密码到指定邮箱
    try:
        msg = Message('管理员动态登录密码', recipients=['你的邮箱@126.com'])
        msg.body = f'您的管理员动态登录密码是：{admin_dynamic_code}，有效期12小时。\n\n请使用此密码和管理员用户名登录。\n\n注意：此密码仅用于登录验证，不是您的实际管理员密码。'
        mail.send(msg)
        flash('动态密码已发送到管理员邮箱')
    except Exception as e:
        app.logger.error(f'发送管理员动态密码邮件失败: {str(e)}')
        flash('发送动态密码失败，请稍后再试')

    return redirect(url_for('admin_login'))

# 管理员登录路由
@app.route('/admin_login', methods=['GET', 'POST'])
def admin_login():
    """管理员登录路由

    Returns:
        render_template: GET请求返回管理员登录表单
        redirect: POST请求处理登录并 redirect到管理员面板或重新登录
    """
    if current_user.is_authenticated and getattr(current_user, 'is_admin', False):
        return redirect(url_for('admin_dashboard'))
    elif current_user.is_authenticated:
        # 非管理员用户，强制注销
        logout_user()
        flash('非管理员用户禁止访问')

    if request.method == 'POST':
        username = request.form['username']
        password = request.form['password']
        dynamic_code = request.form['dynamic_code']

        # 验证动态码
        global admin_code_store
        if not admin_code_store or 'code' not in admin_code_store or 'expiry' not in admin_code_store:
            flash('动态密码不存在或已过期，请重新获取')
            return redirect(url_for('admin_login'))

        if dynamic_code != admin_code_store['code'] or datetime.now(datetime.UTC) > admin_code_store['expiry']:
            flash('动态密码无效或已过期，请重新获取')
            return redirect(url_for('admin_login'))

        # 动态密码验证通过
        user = User.query.filter_by(username=username).first()
        if user and user.check_password(password) and user.is_admin:
            # 验证通过，清除存储的动态密码
            admin_code_store = None
            login_user(user)
            return redirect(url_for('admin_dashboard'))

        flash('管理员登录失败，用户名或密码错误')
        return redirect(url_for('admin_login'))

    return render_template('admin_login.html')

# 管理员仪表板路由
@app.route('/admin_dashboard')
@login_required
def admin_dashboard():
    """管理员仪表板路由

    Returns:
        render_template: 返回管理员仪表板模板
        redirect: 如果非管理员用户访问，重定向到首页
    """
    if not getattr(current_user, 'is_admin', False):
        flash('非管理员用户禁止访问')
        return redirect(url_for('index'))

    # 获取所有非admin用户
    users = User.query.filter(User.username != 'admin').all()
    return render_template('admin_dashboard.html', users=users)

# 管理员查看/重置用户密码
@app.route('/reset_user_password/<int:user_id>')
@login_required
def reset_user_password(user_id):
    """重置用户密码（管理员功能）

    Args:
        user_id (int): 用户ID

    Returns:
        redirect: 重定向到管理员面板
    """
    if not getattr(current_user, 'is_admin', False):
        flash('非管理员用户禁止访问')
        return redirect(url_for('index'))

    user = User.query.get_or_404(user_id)
    # 生成新密码
    new_password = secrets.token_urlsafe(12)
    user.set_password(new_password)
    db.session.commit()

    flash(f'用户 {user.username} 的密码已重置为: {new_password}')
    return redirect(url_for('admin_dashboard'))

# 管理员删除用户
@app.route('/delete_user/<int:user_id>')
@login_required
def delete_user(user_id):
    """删除用户（管理员功能）

    Args:
        user_id (int): 用户ID

    Returns:
        redirect: 重定向到管理员面板
    """
    if not getattr(current_user, 'is_admin', False):
        flash('非管理员用户禁止访问')
        return redirect(url_for('index'))

    if user_id == current_user.id:
        flash('不能删除当前登录的管理员账号')
        return redirect(url_for('admin_dashboard'))

    user = User.query.get_or_404(user_id)
    user_folder = os.path.join(app.config['UPLOAD_FOLDER'], str(user_id))

    try:
        # 删除用户所有文件记录
        files = File.query.filter_by(user_id=user_id).all()
        for file in files:
            if os.path.exists(file.filepath):
                os.remove(file.filepath)
            db.session.delete(file)
        # 删除用户
        db.session.delete(user)
        db.session.commit()
        flash(f'用户 {user.username} 已成功删除')
    except Exception as e:
        db.session.rollback()
        flash(f'删除用户失败: {str(e)}')

    return redirect(url_for('admin_dashboard'))

# 解除用户封禁
@app.route('/unban_user/<int:user_id>')
@login_required
def unban_user(user_id):
    """解除用户封禁（管理员功能）

    Args:
        user_id (int): 用户ID

    Returns:
        redirect: 重定向到管理员面板
    """
    if not getattr(current_user, 'is_admin', False):
        flash('非管理员用户禁止访问')
        return redirect(url_for('index'))

    user = User.query.get_or_404(user_id)
    user.is_banned = False
    db.session.commit()
    flash(f'用户 {user.username} 已解除封禁')
    return redirect(url_for('admin_dashboard'))

# 查看用户分享的文件
@app.route('/user_shared_files/<int:user_id>')
@login_required
def user_shared_files(user_id):
    """查看用户分享的文件（管理员功能）

    Args:
        user_id (int): 用户ID

    Returns:
        render_template: 返回用户分享文件列表模板
    """
    if not getattr(current_user, 'is_admin', False):
        flash('非管理员用户禁止访问')
        return redirect(url_for('index'))

    user = User.query.get_or_404(user_id)
    # 获取用户所有分享的文件
    shared_files = File.query.filter_by(user_id=user_id).filter(File.share_token.isnot(None)).all()
    return render_template('user_shared_files.html', user=user, shared_files=shared_files)

# 取消文件分享
@app.route('/cancel_file_share/<int:file_id>')
@login_required
def cancel_file_share(file_id):
    """取消文件分享（管理员功能）

    Args:
        file_id (int): 文件ID

    Returns:
        redirect: 重定向到用户分享文件列表
    """
    if not getattr(current_user, 'is_admin', False):
        flash('非管理员用户禁止访问')
        return redirect(url_for('index'))

    file = File.query.get_or_404(file_id)
    user_id = file.user_id
    file.share_token = None
    file.share_expiry = None
    db.session.commit()
    flash(f'文件 {file.filename} 的分享已取消')
    return redirect(url_for('user_shared_files', user_id=user_id))

@app.route('/cancel_account')
@login_required
def cancel_account():
    """注销用户账户

    Returns:
        redirect: 重定向到登录页面或仪表盘
    """
    user = current_user
    user_id = user.id
    user_folder = os.path.join(app.config['UPLOAD_FOLDER'], str(user_id))

    try:
        # 删除用户所有文件记录
        files = File.query.filter_by(user_id=user_id).all()
        for file in files:
            if os.path.exists(file.filepath):
                os.remove(file.filepath)
            db.session.delete(file)
        # 删除用户
        db.session.delete(user)
        db.session.commit()
        flash('账号已成功注销，所有数据已删除')
        return redirect(url_for('login'))
    except Exception as e:
        db.session.rollback()
        flash(f'注销账号时出错: {str(e)}')
        app.logger.error(f'注销账号错误: {str(e)}')
        return redirect(url_for('dashboard'))

# 绑定邮箱路由
@app.route('/bind_email', methods=['GET', 'POST'])
@login_required
def bind_email():
    """绑定用户邮箱

    Returns:
        render_template: GET请求返回邮箱绑定表单
        redirect: POST请求处理绑定并 redirect到仪表盘
    """
    user = current_user
    # 如果用户已经绑定邮箱，重定向到个人网盘并显示提示
    if user.email and user.email.strip():
        flash('您已绑定邮箱，无需再次绑定')
        return redirect(url_for('dashboard'))

    if request.method == 'POST':
        email = request.form.get('email')
        if not email or not email.strip():
            flash('邮箱不能为空')
            return redirect(url_for('bind_email'))

        # 检查邮箱是否已被其他用户使用
        existing_user = User.query.filter_by(email=email).first()
        if existing_user:
            flash('该邮箱已被其他账号绑定')
            return redirect(url_for('bind_email'))

        # 更新用户邮箱
        user.email = email
        db.session.commit()
        flash('邮箱绑定成功')
        return redirect(url_for('dashboard'))

    return render_template('bind_email.html')

# 继续cancel_account函数的实现
    try:
        # 删除用户所有文件记录
        files = File.query.filter_by(user_id=user_id).all()
        for file in files:
            if os.path.exists(file.filepath):
                os.remove(file.filepath)
            db.session.delete(file)
        db.session.commit()

        # 删除用户文件夹
        if os.path.exists(user_folder):
            shutil.rmtree(user_folder)

        # 删除用户
        db.session.delete(user)
        db.session.commit()

        # 注销用户
        logout_user()
        flash('账号已成功注销，所有数据已删除')
        return redirect(url_for('index'))
    except Exception as e:
        db.session.rollback()
        flash(f'注销账号时出错: {str(e)}')
        app.logger.error(f'注销账号错误: {str(e)}')
        return redirect(url_for('dashboard'))

@app.route('/dashboard')
@login_required
def dashboard():
    """用户仪表盘路由

    Returns:
        render_template: 返回用户仪表盘模板，包含文件列表
    """
    files = File.query.filter_by(user_id=current_user.id).all()
    return render_template('dashboard.html', files=files)

@app.route('/upload', methods=['POST'])
@login_required
def upload():
    """文件上传功能

    Returns:
        redirect: 上传成功或失败后重定向到仪表盘
    """
    if 'file' not in request.files:
        flash('没有文件部分')
        return redirect(url_for('dashboard'))

    file = request.files['file']
    if file.filename == '':
        flash('没有选择文件')
        return redirect(url_for('dashboard'))

    if file:
        filename = file.filename
        # 确保文件名唯一
        user_folder = os.path.join(app.config['UPLOAD_FOLDER'], str(current_user.id))
        while os.path.exists(os.path.join(user_folder, filename)):
            # 为重复文件名添加唯一标识符，但保留原始文件名（包括中文）
            name, ext = os.path.splitext(filename)
            filename = f"{name}_{secrets.token_hex(4)}{ext}"
        # 确保目录存在
        os.makedirs(user_folder, exist_ok=True)
        filepath = os.path.join(user_folder, filename)
        file.save(filepath)

        # 获取文件大小
        size = os.path.getsize(filepath)

        # 保存文件信息到数据库
        new_file = File(filename=filename, filepath=filepath, size=size, user_id=current_user.id)
        db.session.add(new_file)
        db.session.commit()

        flash('文件上传成功')
        return redirect(url_for('dashboard'))

@app.route('/download/<int:file_id>')
@login_required
def download(file_id):
    """文件下载功能

    Args:
        file_id (int): 文件ID

    Returns:
        send_from_directory: 返回文件下载响应
        redirect: 无权访问或出错时重定向到仪表盘
    """
    file = File.query.get_or_404(file_id)
    if file.user_id != current_user.id:
        flash('无权访问此文件')
        return redirect(url_for('dashboard'))

    return send_from_directory(os.path.dirname(file.filepath), os.path.basename(file.filepath), as_attachment=True)

@app.route('/delete/<int:file_id>')
@login_required
def delete(file_id):
    """文件删除功能

    Args:
        file_id (int): 文件ID

    Returns:
        redirect: 删除成功或失败后重定向到仪表盘
    """
    file = File.query.get_or_404(file_id)
    if file.user_id != current_user.id:
        flash('无权删除此文件')
        return redirect(url_for('dashboard'))

    try:
        # 检查文件是否存在
        if os.path.exists(file.filepath):
            # 删除文件
            os.remove(file.filepath)
            # 从数据库删除记录
            db.session.delete(file)
            db.session.commit()
            flash('文件已删除')
        else:
            flash('文件不存在')
    except Exception as e:
        flash(f'删除文件时出错: {str(e)}')
        app.logger.error(f'删除文件错误: {str(e)}')

    return redirect(url_for('dashboard'))

@app.route('/share/<int:file_id>')
@login_required
def share(file_id):
    """文件分享功能

    Args:
        file_id (int): 文件ID

    Returns:
        redirect: 分享成功后重定向到仪表盘
    """
    file = File.query.get_or_404(file_id)
    if file.user_id != current_user.id:
        flash('无权分享此文件')
        return redirect(url_for('dashboard'))

    # 生成分享token和过期时间（7天）
    file.share_token = str(uuid.uuid4())
    file.share_expiry = datetime.datetime.utcnow() + datetime.timedelta(days=7)
    db.session.commit()

    # 生成分享链接
    share_link = url_for('shared_file', token=file.share_token, _external=True)
    flash(f'分享链接已生成: {share_link}')
    return redirect(url_for('dashboard'))

@app.route('/shared/<token>')
def shared_file(token):
    """访问分享文件

    Args:
        token (str): 分享token

    Returns:
        send_from_directory: 返回分享文件下载响应
        redirect: 无效或过期链接时重定向到首页
    """
    file = File.query.filter_by(share_token=token).first()
    if not file or file.share_expiry < datetime.datetime.utcnow():
        flash('无效或已过期的分享链接')
        return redirect(url_for('index'))

    return send_from_directory(os.path.dirname(file.filepath), os.path.basename(file.filepath), as_attachment=True)

# 添加验证码功能所需的导入
import random
import string

# 已删除重复的forgot_password函数定义

# 生成管理员动态密码
@app.route('/generate_admin_code', methods=['POST'])
def generate_admin_code():
    """生成管理员动态登录密码

    Returns:
        redirect: 重定向到管理员登录页面
    """
    # 检查请求是否来自管理员登录页面
    # 生成12小时有效的动态密码
    admin_dynamic_code = ''.join(random.choices(string.ascii_uppercase + string.digits, k=10))
    expiry_time = datetime.now(datetime.UTC) + datetime.timedelta(hours=12)

    # 存储动态密码和过期时间
    # 在实际应用中，应该使用数据库或缓存来存储
    # 这里为了简化，使用全局变量（不推荐在生产环境使用）
    global admin_code_store
    admin_code_store = {
        'code': admin_dynamic_code,
        'expiry': expiry_time
    }

    # 发送动态密码到指定邮箱
    try:
        msg = Message('管理员动态登录密码', recipients=['你的邮箱@126.com'])
        msg.body = f'您的管理员动态登录密码是：{admin_dynamic_code}，有效期12小时。\n\n请使用此密码和管理员用户名登录。\n\n注意：此密码仅用于登录验证，不是您的实际管理员密码。'
        mail.send(msg)
        flash('动态密码已发送到管理员邮箱')
    except Exception as e:
        app.logger.error(f'发送管理员动态密码邮件失败: {str(e)}')
        flash('发送动态密码失败，请稍后再试')

    return redirect(url_for('admin_login'))

# 验证管理员动态密码
@app.route('/verify_admin_code', methods=['POST'])
def verify_admin_code():
    """验证管理员动态登录密码

    Returns:
        redirect: 验证成功或失败后重定向到管理员登录页面
    """
    global admin_code_store
    code = request.form.get('dynamic_code')

    if not admin_code_store or 'code' not in admin_code_store or 'expiry' not in admin_code_store:
        flash('动态密码不存在或已过期')
        return redirect(url_for('admin_login'))

    if code != admin_code_store['code'] or datetime.now(datetime.UTC) > admin_code_store['expiry']:
        flash('动态密码无效或已过期')
        return redirect(url_for('admin_login'))

    # 动态密码验证通过，清除存储的密码
    admin_code_store = None
    flash('动态密码验证通过，请输入管理员用户名和密码')
    return redirect(url_for('admin_login'))

# 备份用户数据为zip文件
@app.route('/backup_user/<int:user_id>')
@login_required
def backup_user(user_id):
    """备份用户数据为ZIP文件并发送到管理员邮箱

    Args:
        user_id (int): 用户ID

    Returns:
        redirect: 备份成功或失败后重定向到管理员仪表盘
    """
    if not getattr(current_user, 'is_admin', False):
        flash('非管理员用户禁止访问')
        return redirect(url_for('index'))

    user = User.query.get_or_404(user_id)
    user_folder = os.path.join(app.config['UPLOAD_FOLDER'], str(user.id))

    if not os.path.exists(user_folder):
        flash('用户文件夹不存在')
        return redirect(url_for('admin_dashboard'))

    # 创建临时zip文件
    import tempfile
    import zipfile

    with tempfile.NamedTemporaryFile(delete=False, suffix='.zip') as temp_zip:
        with zipfile.ZipFile(temp_zip, 'w', zipfile.ZIP_DEFLATED) as zipf:
            for root, dirs, files in os.walk(user_folder):
                for file in files:
                    file_path = os.path.join(root, file)
                    arcname = os.path.relpath(file_path, user_folder)
                    zipf.write(file_path, arcname)

    # 发送zip文件给管理员
    try:
        msg = Message(f'用户 {user.username} 数据备份', recipients=['你的邮箱@126.com'])
        with open(temp_zip.name, 'rb') as f:
            msg.attach(f'user_{user.username}_backup.zip', 'application/zip', f.read())
        mail.send(msg)
        flash(f'用户 {user.username} 的数据已备份并发送到管理员邮箱')
    except Exception as e:
        app.logger.error(f'发送用户备份邮件失败: {str(e)}')
        flash(f'备份用户数据失败: {str(e)}')
    finally:
        # 删除临时文件
        os.unlink(temp_zip.name)

    return redirect(url_for('admin_dashboard'))

# 导入zip文件到用户
@app.route('/import_user_data/<int:user_id>', methods=['GET', 'POST'])
@login_required
def import_user_data(user_id):
    """从ZIP文件导入数据到用户账户

    Args:
        user_id (int): 用户ID

    Returns:
        redirect: 导入成功或失败后重定向到管理员仪表盘
        render_template: GET请求返回导入表单
    """
    if not getattr(current_user, 'is_admin', False):
        flash('非管理员用户禁止访问')
        return redirect(url_for('index'))

    user = User.query.get_or_404(user_id)
    user_folder = os.path.join(app.config['UPLOAD_FOLDER'], str(user.id))
    os.makedirs(user_folder, exist_ok=True)

    if request.method == 'POST':
        if 'zip_file' not in request.files:
            flash('没有文件部分')
            return redirect(url_for('import_user_data', user_id=user_id))

        file = request.files['zip_file']
        if file.filename == '':
            flash('没有选择文件')
            return redirect(url_for('import_user_data', user_id=user_id))

        if file and file.filename.endswith('.zip'):
            # 保存上传的zip文件
            with tempfile.NamedTemporaryFile(delete=False, suffix='.zip') as temp_zip:
                file.save(temp_zip.name)

            try:
                # 解压zip文件到用户文件夹
                with zipfile.ZipFile(temp_zip.name, 'r') as zipf:
                    zipf.extractall(user_folder)

                # 更新数据库中的文件记录
                # 先删除旧文件记录
                File.query.filter_by(user_id=user.id).delete()
                db.session.commit()

                # 添加新文件记录
                for root, dirs, files in os.walk(user_folder):
                    for file in files:
                        file_path = os.path.join(root, file)
                        size = os.path.getsize(file_path)
                        new_file = File(
                            filename=file,
                            filepath=file_path,
                            size=size,
                            user_id=user.id
                        )
                        db.session.add(new_file)
                db.session.commit()

                flash(f'数据已成功导入到用户 {user.username} 的账户')
            except Exception as e:
                db.session.rollback()
                app.logger.error(f'导入用户数据失败: {str(e)}')
                flash(f'导入用户数据失败: {str(e)}')
            finally:
                # 删除临时文件
                os.unlink(temp_zip.name)

            return redirect(url_for('admin_dashboard'))
        else:
            flash('请上传有效的zip文件')
            return redirect(url_for('import_user_data', user_id=user_id))

    return render_template('import_user_data.html', user=user)

# 修改邮箱客户端授权码
@app.route('/update_email_authorization', methods=['GET', 'POST'])
@login_required
def update_email_authorization():
    """修改邮箱客户端授权码（管理员功能）

    Returns:
        redirect: 更新成功或失败后重定向到管理员仪表盘
        render_template: GET请求返回表单
    """
    if not getattr(current_user, 'is_admin', False):
        flash('非管理员用户禁止访问')
        return redirect(url_for('index'))

    if request.method == 'POST':
        new_authorization_code = request.form['authorization_code']

        # 更新邮箱授权码
        app.config['MAIL_PASSWORD'] = new_authorization_code
        flash('邮箱客户端授权码已更新')
        return redirect(url_for('admin_dashboard'))

    return render_template('update_email_authorization.html')

# 封禁用户网盘
@app.route('/ban_user/<int:user_id>')
@login_required
def ban_user(user_id):
    """封禁用户网盘（管理员功能）

    Args:
        user_id (int): 用户ID

    Returns:
        redirect: 封禁成功后重定向到管理员仪表盘
    """
    if not getattr(current_user, 'is_admin', False):
        flash('非管理员用户禁止访问')
        return redirect(url_for('index'))

    user = User.query.get_or_404(user_id)
    user.is_banned = True
    db.session.commit()

    # 使所有分享链接无效
    File.query.filter_by(user_id=user_id).update({
        'share_token': None,
        'share_expiry': None
    })
    db.session.commit()

    flash(f'用户 {user.username} 已被封禁，所有分享链接已失效')
    return redirect(url_for('admin_dashboard'))

# 全局变量存储管理员动态密码
admin_code_store = None

# 初始化管理员账号
@app.cli.command('create-admin')
def create_admin():
    """命令行命令：创建或更新管理员账号

    运行方式: flask create-admin
    功能: 检查是否存在管理员账号，存在则更新密码，不存在则创建新账号
    """
    with app.app_context():
        # 检查是否已存在管理员账号
        admin = User.query.filter_by(is_admin=True).first()
        if admin:
            print('管理员账号已存在，将更新密码')
            # 生成新密码
            password = secrets.token_urlsafe(16)
            admin.set_password(password)
            db.session.commit()
        else:
            # 创建管理员账号
            username = 'admin'
            email = ''
            # 生成随机密码
            password = secrets.token_urlsafe(16)

            admin = User(username=username, email=email, is_admin=True)
            admin.set_password(password)
            db.session.add(admin)
            db.session.commit()

            print(f'管理员账号创建成功:')
        username = admin.username

        print(f'用户名: {username}')
        print(f'密码: {password}')
        print(f'请妥善保管此密码，登录后可修改')

        # 将管理员密码保存到文件
        with open('admin_credentials.txt', 'w', encoding='utf-8') as f:
            f.write(f'管理员账号信息\n')
            f.write(f'用户名: {username}\n')
            f.write(f'密码: {password}\n')
            f.write(f'创建时间: {datetime.now().strftime("%Y-%m-%d %H:%M:%S")}\n')
        print(f'管理员账号信息已保存到 admin_credentials.txt 文件')

@app.route('/forgot_password', methods=['GET', 'POST'])
def forgot_password():
    """密码找回功能

    Returns:
        redirect: POST请求处理后重定向到验证码页面或保持在当前页面
        render_template: GET请求返回密码找回表单
    """
    if request.method == 'POST':
        username = request.form.get('username')
        user = User.query.filter_by(username=username).first()
        if user:
            if user.email:
                # 生成验证码
                verification_code = str(random.randint(100000, 999999))
                # 存储验证码和过期时间
                verify_codes[username] = {
                    'code': verification_code,
                    'expiry': datetime.now() + timedelta(minutes=10)
                }
                # 发送邮件
                msg = Message('密码重置验证码', recipients=[user.email])
                msg.body = f'您的验证码是: {verification_code}\n有效期为10分钟。'
                try:
                    mail.send(msg)
                    flash('验证码已发送到您的邮箱，请查收')
                    return redirect(url_for('verify_code', username=username))
                except Exception as e:
                    app.logger.error(f'发送邮件失败: {str(e)}')
                    flash('发送邮件失败，请稍后再试')
                    return render_template('forgot_password.html')
            else:
                flash('该用户未绑定邮箱，请联系管理员')
                return render_template('forgot_password.html')
        else:
            flash('未找到该用户')
            return render_template('forgot_password.html')
    # GET请求返回表单
    return render_template('forgot_password.html')

# 验证码验证路由
@app.route('/verify_code/<username>', methods=['GET', 'POST'])
def verify_code(username):
    """验证码验证功能

    Args:
        username (str): 用户名

    Returns:
        redirect: 验证成功重定向到密码重置页面，失败则重定向到验证码页面
        render_template: GET请求返回验证码表单
    """
    user = User.query.filter_by(username=username).first()
    if not user:
        flash('无效的用户名')
        return redirect(url_for('forgot_password'))

    if request.method == 'POST':
        code = request.form['code']
        
        # 验证验证码
        # 确保user.reset_token_expiry是带时区的datetime对象
        if user.reset_token == code and user.reset_token_expiry.replace(tzinfo=datetime.UTC) > datetime.now(datetime.UTC):
            # 验证码有效，生成新的重置令牌
            reset_token = secrets.token_urlsafe(32)
            user.reset_token = reset_token
            user.reset_token_expiry = datetime.datetime.now(datetime.UTC) + datetime.timedelta(hours=1)
            db.session.commit()
            
            # 重定向到密码重置页面
            return redirect(url_for('reset_password', token=reset_token))
        else:
            flash('验证码无效或已过期')
            return redirect(url_for('verify_code', username=username))

    return render_template('verify_code.html', username=username)

@app.route('/reset_password/<token>', methods=['GET', 'POST'])
def reset_password(token):
    """密码重置功能

    Args:
        token (str): 密码重置令牌

    Returns:
        redirect: 重置成功后重定向到登录页面
        render_template: GET请求返回密码重置表单
    """
    user = User.query.filter_by(reset_token=token).first()

    if not user or user.reset_token_expiry < datetime.datetime.utcnow():
        flash('无效或已过期的重置令牌')
        return redirect(url_for('forgot_password'))

    if request.method == 'POST':
        new_password = request.form['password']
        user.set_password(new_password)
        user.reset_token = None
        user.reset_token_expiry = None
        db.session.commit()

        flash('密码已成功重置，请登录')
        return redirect(url_for('login'))

    return render_template('reset_password.html', token=token)

# 创建数据库表并添加缺失字段
with app.app_context():
    """应用启动时创建数据库表并添加缺失字段

    功能: 检查file表是否存在，如不存在则创建
         检查share_token和share_expiry字段是否存在，如不存在则添加
    """
    # 先检查是否存在file表
    from sqlalchemy import inspect
    inspector = inspect(db.engine)
    if 'file' in inspector.get_table_names():
        # 检查字段是否存在
        columns = [col['name'] for col in inspector.get_columns('file')]
        if 'share_token' not in columns:
            db.session.execute(text('ALTER TABLE file ADD COLUMN share_token VARCHAR(100)'))
            db.session.commit()
        if 'share_expiry' not in columns:
            db.session.execute(text('ALTER TABLE file ADD COLUMN share_expiry DATETIME'))
            db.session.commit()
    # 创建所有表
    db.create_all()

# 应用启动时更新管理员凭证
with app.app_context():
    """应用启动时更新管理员凭证

    功能: 确保admin用户存在且is_admin属性为True
         生成新的管理员密码并更新
         调用update_admin_credentials函数保存凭证
    """
    # 先按用户名查找admin用户
    admin = User.query.filter_by(username='admin').first()
    if admin:
        # 确保admin用户的is_admin属性为True
        if not admin.is_admin:
            admin.is_admin = True
            db.session.commit()
        # 生成新密码
        password = secrets.token_urlsafe(16)
        admin.set_password(password)
        db.session.commit()
        update_admin_credentials(admin.username, password)
    else:
        # 创建管理员账号
        username = 'admin'
        email = ''
        password = secrets.token_urlsafe(16)
        admin = User(username=username, email=email, is_admin=True)
        admin.set_password(password)
        db.session.add(admin)
        db.session.commit()
        update_admin_credentials(username, password)

    # 验证管理员用户是否正确创建
    verified_admin = User.query.filter_by(username='admin').first()
    if verified_admin and verified_admin.is_admin:
        print(f'管理员用户 {verified_admin.username} 验证成功，is_admin={verified_admin.is_admin}')
    else:
        print('警告：管理员用户创建或验证失败')

# 导入配置加载器
from config_loader import app_config

if __name__ == '__main__':
    app.run(
        host=app_config['host'],
        port=app_config['port'],
        debug=app_config['debug']
    )